![]() |
![]() |
|
Nutzen Sie eine generische Klasse wie Stack(Of T), und teilen Sie dem Compiler mit, durch welchen Datentyp T ersetzt werden soll. T wird auch als generischer Typparameter bezeichnet. In der folgenden Anweisung handelt es sich um Integer:
Alle Methoden der Klasse, die T als Parameter definieren oder zurückgeben, akzeptieren jetzt nur noch Integerwerte. Den Zugriff auf die generische Klasse Stack(Of T) zeigt nachfolgend die Methode Main. Der Code ist in einen Try-Block gefasst, um ausgelöste Ausnahmen behandeln zu können. In Kapitel 9 werden wir das Thema Ausnahmen (Exceptions) noch ausgiebig behandeln.
Mehrere TypparameterDie zuvor verwendete generische Klasse Stack benötigte einen variablen Datentyp. Je nachdem, wie die zu entwickelnde Klasse aussehen soll, kann der Bedarf an Platzhaltern jedoch variieren. Ein typisches Beispiel dazu wäre eine Dictionary-Klasse, die nicht nur hinsichtlich des Schlüssels, sondern auch hinsichtlich des zugeordneten Werts individuell typisiert werden soll. In solchen Fällen erlaubt Visual Basic 2005 die Angabe mehrerer Platzhalter, die innerhalb der runden Klammer mittels Kommata getrennt werden.
Der Zugriff auf eine Klasse mit mehreren Typparametern erfolgt genauso wie oben am Beispiel der Klasse Stack gezeigt. Jedem typisierten Parameter wird ein bekannter Typ in runden Klammern übergeben:
7.3.4 Typparameter mit Constraints einschränken
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Public Class GenericClass(Of T) |
| ... |
| End Class |
teilen wir dem Compiler mit, dass der spätere Datentyp zum Zeitpunkt der Klassenentwicklung noch völlig unbekannt ist. Es kann jeder x-beliebige Datentyp verwendet werden.
Wollen Sie innerhalb des Codes der generischen Klasse ein bestimmtes Klassenmitglied des verwendeten Typs aufrufen (beispielsweise eine Methode), ist eine explizite und damit auch unsichere Konvertierung notwendig. Eventuelle Fehler, weil der verwendete Datentyp dieses Klassenmitglied nicht veröffentlicht, würden erst zur Laufzeit der Anwendung erkannt.
Um die Problematik zu verstehen, sehen Sie sich das folgende Beispiel an. Die Klasse SortedElements(Of T) hat eine Methode Add, mit der ein Element der Auflistung hinzugefügt wird. Das neue Element soll nach typspezifischen Kriterien einsortiert werden. Dazu ist ein Vergleich mit den schon enthaltenen Elementen notwendig, zu dem die Methode CompareTo der Schnittstelle IComparable genutzt wird.
| Public Class SortedElements(Of T) |
| Dim arr(100) As T |
| Public Function Add(ByVal element As T) As T() |
| Dim i As Integer |
| For i = 0 To arr.Length – 1 |
| Dim result As Integer = _ |
| CType(element, IComparable).CompareTo(arr(i)) |
| ... |
| Next |
| ... |
| End Function |
| End Class |
Implementiert der Datentyp IComparable nicht, wird eine Ausnahme ausgelöst. Um diesem Problem aus dem Weg zu gehen, lassen sich die generische Typparameter mit einer Einschränkung versehen, wie im Folgenden gezeigt wird:
| Public Class SortedElements(Of T As IComparable) |
| ... |
| End Class |
Nun ist eine Bedingung festgelegt, an die sich der spätere konkrete Typ halten muss: Er muss die aufgeführte Schnittstelle IComparable unterstützen. Wird im Code ein Typ angegeben, der diese Schnittstelle nicht unterstützt, meldet der Compiler einen Fehler.
Eine Bedingung ist nicht auf Schnittstellen beschränkt; Sie können auch eine Klasse angeben und legen damit die Basisklasse des an den Typparameter T übergebenen konkreten Typs fest.
Typparameter, die keine Einschränkungen aufweisen, werden als ungebundene Typparameter bezeichnet, mit Einschränkungen als gebundene Typparameter. Im Bedarfsfall dürfen Sie auch mehrere Einschränkungen angeben, die durch geschweifte Klammern hinter As angegeben werden.
| Public Class SortedElements(Of T _ |
| As {IComparable, IDisposable, BaseClass}) |
| ... |
| End Class |
Nehmen wir an, Sie möchten in einer generischen Klasse ein Objekt vom Typ eines generischen Typparameters erzeugen. Das Problem dabei ist, dass der VB-Compiler nicht weiß, ob der Typparameter einen passenden Konstruktor hat. Die Folge wäre ein Kompilierfehler.
Um in dieser Situation eine Lösung zu bieten, können Sie als Einschränkung New angeben, wie im folgenden Codefragment gezeigt wird:
| Public Class ClassA(Of T As New) |
| ... |
| Public Sub New() |
| Dim obj As New T |
| End Sub |
| End Class |
Sie treffen damit eine entscheidende Aussage: Der gewählte Argumenttyp muss einen öffentlichen, parameterlosen Konstruktor unterstützen. Einen parametrisierten Konstruktor vorzuschreiben ist nicht möglich.
Generische Typen sind nicht nur im Zusammenhang mit Klassen möglich, sondern auch mit Methoden. Dabei ist es nicht zwingend notwendig, dass die Typparameter einer Methode denen der Klasse entsprechen:
| Public Class ClassA(Of T) |
| Public Sub GenericMethod(Of K)(obj As K) |
| ... |
| End Sub |
| End Class |
Im Gültigkeitsbereich der Klasse ist in diesem Fall der Typ T bekannt, K nur innerhalb der Methode.
Sie dürfen generische, methodenspezifische Typparameter auch angeben, wenn die Klasse selbst keine definiert:
| Public Class ClassA |
| Public Sub GenericMethod(Of T)(ByVal x As T) |
| Console.WriteLine("Operation in generischer Methode") |
| End Sub |
| End Class |
Der Aufruf einer Methode mit generischen Typparametern ist sehr einfach. Sie instanziieren in gewohnter Weise zuerst die Klasse und rufen die Methode unter Angabe des gewünschten konkreten Datentyps auf:
| Dim obj As New ClassA |
| obj.GenericMethod(Of Integer)(23) |
Sie können sogar auf die Typangabe verzichten, denn auch in diesem Fall wird der VB-Compiler die richtige Schlussfolgerung ziehen:
| obj.GenericMethod(25) |
Dieser Aufruf ist absolut gleichwertig.
Muss der generische Typparameter einer Methode bestimmten Bedingungen genügen, legen Sie einen Constraint fest. Die Syntax entspricht der der Constraints einer Klasse. Allerdings ist es nicht möglich, einen Constraint für einen generischen Typparameter einer Methode zu definieren, der bereits auf Klassenebene festgelegt ist.
Generische Klassen können abgeleitet werden. Die Regeln sind ähnlich denen, die wir schon kennen. Aufgrund der besonderen Natur generischer Klassen sind dabei jedoch ein paar Besonderheiten zu beachten.
Ist die Basisklasse generisch, kann die abgeleitete Klasse den generischen Typparameter übernehmen und selbst generisch sein.
| Public Class ClassA(Of T) |
| ... |
| End Class |
| Public Class ClassB(Of T) |
| Inherits ClassA(Of T) |
| ... |
| End Class |
Soll die abgeleitete Klasse nicht generisch sein, muss der Typparameter in der Angabe der Basisklasse durch einen konkreten Datentyp ersetzt werden, wie nachfolgend gezeigt:
| Public Class ClassA(Of T As IComparable) |
| ... |
| End Class |
| Public Class ClassB |
| Inherits ClassA(Of Integer) |
| ... |
| End Class |
Sie können umgekehrt auch dann eine generische Subklasse entwickeln, wenn die Basisklasse nicht generisch ist.
Die Generics haben erst in der Version 2.0 ihren Einzug in das .NET Framework gefunden und die Klassenbibliothek beeinflusst. Beispielsweise enthält .NET 2.0 mit System.Collections.Generic einen neuen Namespace, der eine Reihe generischer Auflistungen beinhaltet. Dieser Namespace enthält unter anderem auch eine Klasse Stack(OF T), ähnlich der, die wir anfangs unserer Ausführungen zu den Generics beschrieben haben. Altbekannte Klassen des Frameworks 1.0/1.1 sind um generische Methoden ergänzt worden. Hier sei exemplarisch nur die Klasse Array erwähnt.
Viele andere Klassen und Schnittstellen des Namespace System.Collections finden ein generisches Pendant in System.Collections.Generic. In der folgenden Tabelle sind die wichtigsten Klassen und Schnittstellen des neuen Namespace aufgeführt nebst den nichtgenerischen Implementierungen.
| System.Collections.Generic | System.Collections |
| Collection<T> | CollectionBase |
| Dictionary<K, V> | Hashtable |
| IComparer<T> | IComparer |
| IComparable<T> | IComparable |
| IEnumerable<T> | IEnumerable |
| IList<T> | IList |
| List<T> | ArrayList |
| Queue<T> | Queue |
| SortedDictionary<K, V> | SortedList |
| Stack<T> | Stack |
| << zurück |
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken.
Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die
gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich
geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung,
Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.